package xueqiu

import (
	"context"
	"fmt"
	"math"
	"sync"
	"time"

	"gitee.com/lyuanbo/stock-trade/internal/pkg/mq"
	"gitee.com/lyuanbo/stock-trade/internal/pkg/stock/constant"
	"gitee.com/lyuanbo/stock-trade/internal/pkg/stock/pkg"
	"github.com/maypok86/otter"
)

type XueQiu struct {
	token string                                 // xq_a_token
	u     string                                 // 随机数字
	store otter.Cache[string, []*constant.Kline] // TODO: 这里要实现一个缓存的 Builder

	codeIds  []string                   // 股票代码列表, 修改 StockMap 时需要同步更新
	StockMap map[string]*constant.Stock // 股票代码与股票信息的映射

	periods []string // 周期列表
	mq      mq.IMq   // 消息队列
}

// Run 创建 XueQiu 对象
// TODO: 这里要实现一个缓存的 Builder
func Run(ctx context.Context, opts ...Option) error {
	x := &XueQiu{
		StockMap: make(map[string]*constant.Stock),
		token:    "66b44bfc7f3b318b307fde21611b680f87ebbc68", // TODO: 这里应该从配置文件中读取
		//periods:  []string{"day", "5m", "15m", "30m", "60m"},
		periods: []string{"60m"},
	}

	for _, opt := range opts {
		opt(x)
	}

	// 设置股票池
	err := x.SetStocks(nil)
	if err != nil {
		return err
	}

	// 分时行情
	go x.klines(ctx)

	// 实时行情
	go x.listen(ctx)

	return nil
}

func (x *XueQiu) listen(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		default:
			if time.Now().Unix()%3 == 0 {
				// 每次20一组获取行业数据
				var wg sync.WaitGroup
				var codes []string
				for i := 0; i < len(x.codeIds); {
					if stock, ok := x.StockMap[x.codeIds[i]]; ok {
						if !pkg.CheckOpen(stock.Exchange) {
							continue
						}
						if len(codes) < 20 && i < len(x.codeIds) {
							codes = append(codes, x.codeIds[i])
							continue
						}
					}

					wg.Add(1)
					go func() {
						defer wg.Done()

						klinesMap, err := x.Realtime(ctx, codes)
						if err != nil {
							fmt.Println(err)
							return
						}

						for symbol, kline := range klinesMap {
							// 发送通知
							_ = x.mq.Publish(constant.MqTopicTypeRealtime, &mq.Options{Persist: false}, symbol, kline)
						}

					}()

					wg.Wait()
				}
			}

			time.Sleep(1 * time.Second)
		}
	}
}

func (x *XueQiu) klines(ctx context.Context) {
	for symbol := range x.StockMap {
		for _, period := range x.periods {
			go func() {
				key := constant.GetKlineKey(symbol, period)
				m, err := x.Kline(ctx, period, []string{symbol})
				if err != nil {
					fmt.Println(err)
					return
				}
				x.store.Set(key, m[symbol])

				// 历史行情
				// func kline(ctx context.Context, symbol string, history bool)
				_ = x.mq.Publish(constant.MqTopicTypeKline, &mq.Options{Persist: false}, period, symbol)
			}()
		}
	}

	// 订阅实时行情
	_ = x.mq.Subscribe(constant.MqTopicTypeRealtime, "kline", func(symbol string, kline *constant.Kline) error {
		for _, period := range x.periods {
			go func() {
				key := constant.GetKlineKey(symbol, period)

				v, ok := x.store.Get(key)
				if !ok {
					fmt.Println("not exist", key)
					return
				}

				// 获取最后一条
				last := v[len(v)-1]
				if last.Date.Unix() > time.Now().Unix() {
					kline.Date = last.Date
					// 替换
					last.Close = kline.Close
					last.High = math.Max(kline.High, last.High)
					last.Low = math.Min(kline.Low, last.Low)
					last.Volume += kline.Volume
					last.Amount += kline.Amount
					last.Chg = last.Close - last.LastClose
				} else {
					// 倒数第二条
					secondLast := v[len(v)-2]
					// 计算间隔
					n := last.Date.Unix() - secondLast.Date.Unix()
					kline.Date = last.Date.Add(time.Duration(n) * time.Second)

					// 补全咋日价
					kline.LastClose = last.Close
					v = append(v, kline)
				}

				// 保存到缓存
				x.store.Set(key, v)

				// 发送通知

			}()
		}
		return nil
	})
}

// SetStocks 设置股票列表
func (x *XueQiu) SetStocks(stocks []constant.Stock) error {
	if len(stocks) == 0 {
		var err error
		stocks, _, err = x.List()
		if err != nil {
			return err
		}
	}
	for _, stock := range stocks {
		key := fmt.Sprintf("%s%s", stock.Exchange, stock.Code)
		x.codeIds = append(x.codeIds, key)
		x.StockMap[key] = &stock
	}

	return nil
}

// SetPeriods 设置获取数据的周期
func (x *XueQiu) SetPeriods(periods []string) {
	x.periods = periods
}
